﻿using System;
using System.Collections.Generic;
using System.Text;
using Tao.FreeGlut;
using PacMan3D.Utils;
using Tao.OpenGl;
using Tao.DevIl;
using PacMan3D.Objects;
using PacMan3D.Sounds;
using System.Windows.Forms;
using System.Threading;

namespace PacMan3D.Windows
{
    public class Window : IWindow
    {
        private Mode CurrentMode; 

        private MenuWindow MenuWindow;
        private GameWindow GameWindow;
        public IWindow CurrentWindow {get; private set;}

        public int Width { get; set; }
        public int Height { get; set; }


        private Window(int width, int height, string title, string icon)
        {
            // inicializa a largura e altura da janela
            this.Width = width;
            this.Height = height;

            // calcula a posição inicial da janela - centrada
            int initialX = (int)((Screen.PrimaryScreen.Bounds.Width / 2) - (this.Width / 2));
            int initialY = (int)((Screen.PrimaryScreen.Bounds.Height / 2) - (this.Height / 2));

            // cria uma janela:
            // double buffered, 
            // modo de cores RGBA, 
            // activação do buffer de profundidade
            Glut.glutInit();
            Glut.glutInitDisplayMode(Glut.GLUT_DOUBLE | Glut.GLUT_RGBA | Glut.GLUT_DEPTH);
            Glut.glutInitWindowSize(this.Width, this.Height);
            Glut.glutInitWindowPosition(initialX, initialY);
            Glut.glutCreateWindow(title);
        }

        public void StartRendering(Mode mode)
        {
            InitializeOpengGl();

            UpdateCurrentMode(mode);
            
            SubscribeCallbackHandlers();

            Glut.glutMainLoop();
        }

        public void Start()
        {
            return;
        }

        private void SubscribeCallbackHandlers()
        {
            // define os handlers para tratamento de eventos
            Glut.glutDisplayFunc(new Glut.DisplayCallback(DisplayHandler));
            Glut.glutReshapeFunc(new Glut.ReshapeCallback(ReshapeHandler));
            Glut.glutKeyboardFunc(new Glut.KeyboardCallback(KeyboardHandler));
            Glut.glutSpecialFunc(new Glut.SpecialCallback(SpecialHandler));
            Glut.glutSpecialUpFunc(new Glut.SpecialUpCallback(SpecialUpHandler));
            Glut.glutIdleFunc(new Glut.IdleCallback(IdleHandler));
        }

        private void InitializeOpengGl()
        {
            // Enables Smooth Color Shading
            Gl.glShadeModel(Gl.GL_SMOOTH);

            // This will clear the background color to Black
            RGBAColor cc = new RGBAColor(StandardColors.Black, 1.0f);
            Gl.glClearColor(cc.Red, cc.Green, cc.Blue, cc.Alpha);

            // Enables depth testing
            Gl.glEnable(Gl.GL_DEPTH_TEST);

            // Enables clearing of the depth buffer
            Gl.glClearDepth(1.0f);

            // The Type Of Depth Test To Do
            Gl.glDepthFunc(Gl.GL_LEQUAL);

            ///
            /// Para a leitura dos Modelos
            /// 
            Gl.glDepthMask(Gl.GL_TRUE);
            Gl.glEnable(Gl.GL_NORMALIZE);

            ///
            /// Para activar os calculos só para uma das faces
            /// 
            Gl.glEnable(Gl.GL_CULL_FACE);
            Gl.glCullFace(Gl.GL_BACK);

            ///
            /// Para a Iluminação
            /// 
            Gl.glEnable(Gl.GL_COLOR_MATERIAL);

            ///
            /// DevIL, DevILU, DevILUT Initialization (Initialize ILUT with OpenGL support)
            /// 
            Il.ilInit();
            Ilu.iluInit();
            Ilut.ilutInit();
            Ilut.ilutRenderer(Ilut.ILUT_OPENGL);
        }

        public void UpdateCurrentMode(Mode mode)
        {
            if (this.MenuWindow == null)
                this.MenuWindow = new MenuWindow();
            else
                this.MenuWindow.Initialize();

            switch (mode)
            {
                case Mode.Menu:
                    this.CurrentWindow = this.MenuWindow;
                    break;

                case Mode.Game:
                    this.GameWindow = new GameWindow();
                    this.CurrentWindow = this.GameWindow;
                    break;
            }
            
            this.CurrentMode = mode;
            this.CurrentWindow.ReshapeHandler(this.Width, this.Height);
        }

        #region IWindow Members

        public void DisplayHandler() {
           this.CurrentWindow.DisplayHandler();
        }

        public void ReshapeHandler(int width, int height) {
           this.CurrentWindow.ReshapeHandler(width, height);
        }

        public void KeyboardHandler(byte key, int x, int y) {
            this.CurrentWindow.KeyboardHandler(key, x, y);
        }

        public void SpecialHandler(int key, int x, int y) {
            this.CurrentWindow.SpecialHandler(key, x, y);
        }

        public void SpecialUpHandler(int key, int x, int y) {
            this.CurrentWindow.SpecialUpHandler(key, x, y);
        }

        public void IdleHandler() {
            this.CurrentWindow.IdleHandler();
        }
        
        #endregion


        public void ExitGame()
        {
            Thread.Sleep(1000);
            SoundController.Instance().Deinitialize();
            Environment.Exit(0);
        }

        private static Window instance;
        public static Window Instance()
        {
            if (instance == null)
                instance = new Window(Constants.WIN_WIDHT, Constants.WIN_HEIGHT, Constants.WIN_TITLE, Constants.WIN_ICON);

            return instance;
        }
    }

    public enum Mode { Menu = 0, Game = 1}
}
